/**
 *******************************************************************************
 * @FileName  : keyboard.c
 * @Author    : GaoQiu
 * @CreateDate: 2020-05-18
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *******************************************************************************
 */
#include "board/board.h"
#include "keyboard.h"
#include "key_code.h"
#include "platform/platform.h"

KbCb_t kbCb;

#ifndef KB_DRIV_PINS
//#error "keyboard drive(COL) pins undefined..."
uint16_t drivPin[] = {0};
#else
uint16_t drivPin[] = KB_DRIV_PINS;
#endif

#ifndef KB_SCAN_PINS
//#error "keyboard scan(ROW) pins undefined..."
uint16_t scanPin[] = {0};
#else
uint16_t scanPin[] = KB_SCAN_PINS;
#endif

#ifndef KB_MAP_TBL
//#error "Keyboard map table undefined..."
uint8_t keyMap[COUNTOF(scanPin)][COUNTOF(drivPin)] = {{0}};
#else
uint8_t keyMap[COUNTOF(scanPin)][COUNTOF(drivPin)] = KB_MAP_TBL;
#endif

/**
 * @brief  : Keyboard initialization.
 * @param  : none.
 * @return : none.
 */
void KB_Init(void)
{
	//drive pin -> COL
	for(int i=0; i<COUNTOF(drivPin); i++)
	{
		GPIO_InitEx(drivPin[i], GPIO_Mode_In_Out);
		GPIO_SetPullModeEx(drivPin[i], GPIO_PullUp_Strong);
//		GPIO_SetPullModeEx(drivPin[i], GPIO_PullDown);
	}

	//scan pin -> ROW
	for(int i=0; i<COUNTOF(scanPin); i++)
	{
		GPIO_InitEx(scanPin[i], GPIO_Mode_In);
		GPIO_SetPullModeEx(scanPin[i], GPIO_PullDown);
//		GPIO_SetPullModeEx(scanPin[i], GPIO_PullUp_Weak);
	}
}

/**
 * @brief  : Check if key is pressed.
 * @param  : none.
 * @return : return row map for pressed key.
 */
uint32_t KB_CheckKeyPressed(void)
{
	GPIO_Type_t *pGPIOx = NULL;
	uint8_t pinMask = 0;

	//drive pin -> COL
	for(int i=0; i<COUNTOF(drivPin); i++)
	{
		pGPIOx = GPIOA + GPIO_GRP_ID(drivPin[i]);
		pinMask = GPIO_PIN_MASK(drivPin[i]);

		if(KEY_VALID_LEVEL){
			pGPIOx->DO |= pinMask;
		}else{
			pGPIOx->DO &= ~pinMask;
		}
		//pGPIOx->OEN &= ~pinMask;//ENABLE OUTPUT
	}

	delay_us(20);

	uint32_t pressedMask = 0;

	for(int i=0; i<COUNTOF(scanPin); i++)
	{
		if(GPIO_ReadInputBitEx(scanPin[i]) == KEY_VALID_LEVEL){
			pressedMask |= (1<<i);
		}
	}

	//drive pin -> COL
	for(int i=0; i<COUNTOF(drivPin); i++)
	{
		pGPIOx = GPIOA + GPIO_GRP_ID(drivPin[i]);
		pinMask = GPIO_PIN_MASK(drivPin[i]);

		if(!KEY_VALID_LEVEL){
			pGPIOx->DO |= pinMask;
		}else{
			pGPIOx->DO &= ~pinMask;
		}
		//pGPIOx->OEN |= pinMask;//DISABLE OUTPUT
	}
	return pressedMask;
}

/**
 * @brief  : Debounce
 * @param  : pCurMatrix   Pointer point to keyboard matrix.
 * @return :
 */
bool_t KB_DebounceFilt(uint32_t *pCurMatrix)
{
	static uint32_t lastMatrix[COUNTOF(drivPin)] = {0};
	bool_t keyChanged = false;

	for(int i=0; i<COUNTOF(drivPin); i++)
	{
		if(lastMatrix[i] != pCurMatrix[i]){
			keyChanged = true;
		}
		lastMatrix[i] = pCurMatrix[i];
	}
	return keyChanged;
}

/**
 * @brief  : key code remap.
 * @param  : pMatrix   Pointer point to keyboard matrix.
 * @return :
 */
void KB_KeyRemap(uint32_t *pMatrix)
{
	kbCb.cnt = 0;

	for(int i=0; i<COUNTOF(drivPin); i++)//col
	{
		uint32_t rowMask = pMatrix[i];
		for(int r=0; r<COUNTOF(scanPin); r++)
		{
			if(rowMask & (1<<r)){
				kbCb.keyCode[kbCb.cnt++] = keyMap[r][i];
			}
		}
	}
}

/**
 * @brief  : Keyboard scan.
 * @param  : none.
 * @return : TRUE if key changed otherwise FALSE.
 */
bool_t KB_Scan(void)
{
	bool_t keyChanged = false;

	/* debounce for release key. */
	static uint32_t lastKeyRowMask = 0;
	static uint8_t cnt = 0;

	uint32_t keyRowMask = KB_CheckKeyPressed();
	if(keyRowMask || (cnt && lastKeyRowMask))
	{
		if(keyRowMask){
			cnt = 6;
			lastKeyRowMask = keyRowMask;
		}else{
			cnt--;
		}

		uint32_t pressedMatrix[COUNTOF(drivPin)] = {0};

		for(uint32_t j=0; j<COUNTOF(drivPin); j++)
		{
			GPIO_Type_t *pGPIOx = GPIOA + GPIO_GRP_ID(drivPin[j]);
			uint8_t pinMask = GPIO_PIN_MASK(drivPin[j]);

			if(KEY_VALID_LEVEL){
				pGPIOx->DO |= pinMask;
			}else{
				pGPIOx->DO &= ~pinMask;
			}
			//pGPIOx->OEN &= ~pinMask;//ENABLE OUTPUT

			//delay_us(20);

			for(uint32_t i=0; i<COUNTOF(scanPin); i++)
			{
				if(keyRowMask & (1<<i))
				{
					if(GPIO_ReadInputBitEx(scanPin[i]) == KEY_VALID_LEVEL){
						pressedMatrix[j] |= (1<<i);
					}
				}
			}

			if(!KEY_VALID_LEVEL){
				pGPIOx->DO |= pinMask;
			}else{
				pGPIOx->DO &= ~pinMask;
			}
			//pGPIOx->OEN |= pinMask;//DISABLE OUTPUT
		}

		kbCb.cnt = 0;
		keyChanged = KB_DebounceFilt(pressedMatrix);
		if(keyChanged)
		{
			KB_KeyRemap(pressedMatrix);
		}
	}

	return keyChanged;
}


